﻿using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using StackExchange.Redis;

using static StackExchange.Redis.RedisChannel;

namespace Jack.RedisLib
{
    public partial class RedisClient
    {
        public async Task<long> Publish<T>(string channel, T message, PatternMode mode = PatternMode.Auto, int db = -1, CancellationToken cancellationToken = default)
        {
            IDatabase database = await GetConnectedDatabase(db, cancellationToken);
            if (database == null) return -1;

            RedisChannel redisChannel = new((string)ConvertStringKeyToRedisKey(channel), mode);
            RedisValue redisMessage = ConvertObjectValueToRedisValue(message);

            return await database.PublishAsync(redisChannel, redisMessage);
        }

        public async Task SubScribe<T>(string channel, Action<T> messageHandler, PatternMode mode = PatternMode.Auto, object asyncState = null, CancellationToken cancellationToken = default)
            where T : class
        {
            var connection = await GetConnection(cancellationToken);

            RedisChannel redisChannel = new((string)ConvertStringKeyToRedisKey(channel), mode);
            ISubscriber subscriber = connection.GetSubscriber(asyncState);
            await subscriber.SubscribeAsync(redisChannel, (subChannel, redisValue) =>
            {
                T message = ConvertRedisValueToObjectValue<T>(redisValue);
                messageHandler(message);
            });
        }

        public async Task UnsubscribeAll(object asyncState = null, CancellationToken cancellationToken = default)
        {
            var connection = await GetConnection(cancellationToken);
            ISubscriber subscriber = connection.GetSubscriber(asyncState);
            await subscriber.UnsubscribeAllAsync();
        }

        public async Task<string[]> GetSubscriptionChannels(CancellationToken cancellationToken = default)
        {
            var connection = await GetConnection(cancellationToken);
            var endPoints = connection.GetEndPoints();
            var server = connection.GetServer(endPoints.First());
            RedisChannel[] redisChannels = await server.SubscriptionChannelsAsync();

            return redisChannels.Select(x => (string)x).ToArray();
        }

        public async Task<long> GetSubscriptionPatternCount(CancellationToken cancellationToken = default)
        {
            var connection = await GetConnection(cancellationToken);
            var endPoints = connection.GetEndPoints();
            var server = connection.GetServer(endPoints.First());

            return await server.SubscriptionPatternCountAsync();
        }

        public async Task<long> GetSubscriptionSubscriberCount(string channel, CancellationToken cancellationToken = default)
        {
            var connection = await GetConnection(cancellationToken);
            var endPoints = connection.GetEndPoints();
            var server = connection.GetServer(endPoints.First());

            RedisChannel redisChannel = new((string)ConvertStringKeyToRedisKey(channel), PatternMode.Auto);
            return await server.SubscriptionSubscriberCountAsync(redisChannel);
        }
    }
}
